<script setup>

import dialogJson from "@/components/dialog/dialogJson.js";
import {inject, nextTick, onMounted, ref} from "vue";
import { fabric } from 'fabric';
import {baseStore} from "@/store/baseStore.js";
import {CloseBold, Delete} from "@element-plus/icons-vue";
import ImageClassificationService from "@/service/impl/ImageClassificationService.js"


let bs = baseStore()
let {menuFoldState} = storeToRefs(bs)
const props = defineProps({
    editImageItem: {
        type: Object,
        default: () => {
            return dialogJson()
        }
    }
})
let canvas = null,offsetX = 0,offsetY = 0,scaleFactor = 1;
const imageItems = ref([])
const featuresList = ref([])
const emits = defineEmits(['onSave'])
const message = inject('message')
const url = window.webConfig.apiUrl()

const show = () => {
    props.editImageItem.load = true
    props.editImageItem.show = true
    ImageClassificationService.featuresList({
        languageClassificationId: props.editImageItem.data.languageClassificationId
    }).then(res => featuresList.value = res.data)
    if(props.editImageItem.data.featuresConfig) {
        imageItems.value = JSON.parse(JSON.stringify(props.editImageItem.data.featuresConfig))
    }
    nextTick(() => {
        let elementById = document.getElementById("canvas-row");
        canvas = new fabric.Canvas('canvas',{
            // 保持对象的堆叠顺序，防止对象在添加时重新排序
            preserveObjectStacking: true,
            // 启用高分辨率缩放，确保在高 DPI 屏幕上显示清晰。
            enableRetinaScaling: true,
        });
        canvas.setWidth(elementById.clientWidth);
        canvas.setHeight(elementById.clientHeight);
        // 设置背景
        drawBg(url + props.editImageItem.data.imageUrl)
        setTimeout(() => {
            rectangle()
            initRect()
        },200)
        props.editImageItem.load = false
    })
}

const restWindow = (image) => {
    // 监听窗口缩放事件
    window.addEventListener('resize', function () {
        if(!props.editImageItem.show){
            return
        }
        let elementById = document.getElementById("canvas-row");
        canvas.setWidth(elementById.clientWidth);
        canvas.setHeight(elementById.clientHeight);
        const canvasWidth = canvas.getWidth();
        const canvasHeight = canvas.getHeight();
        const imgWidth = image.width;
        const imgHeight = image.height;
        if (imgWidth / canvasWidth > imgHeight / canvasHeight) {
            scaleFactor = canvasWidth / imgWidth;
        } else {
            scaleFactor = canvasHeight / imgHeight;
        }
        image.scale(scaleFactor);
        offsetX = (canvasWidth - image.getScaledWidth()) / 2;
        offsetY = (canvasHeight - image.getScaledHeight()) / 2;
        image.set({ left: offsetX, top: offsetY });
        // 重新计算框选矩形的位置和尺寸
        canvas.getObjects().filter(obj => obj instanceof fabric.Rect || obj instanceof fabric.Text)
            .forEach(obj => {
                let find = imageItems.value.find(item => item.id === obj.id);
                if(obj instanceof fabric.Rect){
                    obj.set({
                        left: find.x * scaleFactor + offsetX,
                        top: find.y * scaleFactor + offsetY,
                        width: find.width * scaleFactor,
                        height: find.height * scaleFactor
                    });
                }
                if(obj instanceof fabric.Text){
                    obj.set({
                        left: find.x * scaleFactor + offsetX,
                        top: find.y * scaleFactor + offsetY - 20,
                    });
                }
            })
        canvas.renderAll();
    });
}

const initRect = () => {
    imageItems.value.forEach(item => {
        let x = Math.floor(item.x * scaleFactor + offsetX)
        let y = Math.floor(item.y * scaleFactor + offsetY)
        const rect = new fabric.Rect({
            id: item.id,
            left: x,
            top: y,
            width: item.width * scaleFactor,
            height: item.height * scaleFactor,
            stroke: 'red',
            fill: 'transparent',
            strokeWidth: 1,
            selectable: true,
        });
        // 创建文本对象
        const text = new fabric.Text(`${item.featuresName}`, {
            id: item.id,
            left: x,
            top: y - 20, // 将文本放置在矩形上方
            fill: 'red',
            fontSize: 15,
            selectable: false
        });
        canvas.add(text);
        canvas.add(rect);
        rectEvent(rect,text)
    })
}

const rectEvent = (rect,text) => {
    // 监听框的缩放事件
    rect.on('scaling', function () {
        const newWidth = rect.getScaledWidth();
        const newHeight = rect.getScaledHeight();
        // console.log('框正在缩放，新的宽度:', newWidth, '新的高度:', newHeight);
    });
    // 监听框的修改完成事件
    rect.on('modified', function () {
        const newWidth = rect.getScaledWidth();
        const newHeight = rect.getScaledHeight();
        // console.log('框修改完成，新的宽度:', newWidth, '新的高度:', newHeight);
        // 更新 imageItems 中的信息
        const relativeLeft = (rect.left - offsetX) / scaleFactor;
        const relativeTop = (rect.top - offsetY) / scaleFactor;
        const relativeWidth = newWidth / scaleFactor;
        const relativeHeight = newHeight / scaleFactor;
        let item = imageItems.value.find(item => item.id === rect.id);
        if (item) {
            item.x = Math.floor(relativeLeft)
            item.y = Math.floor(relativeTop)
            item.width = Math.floor(relativeWidth)
            item.height = Math.floor(relativeHeight)
        }
    });
    // 监听框的移动事件
    rect.on('moving', function () {
        let x = Math.floor(rect.left)
        let y = Math.floor(rect.top - 20)
        // let texts = text.text.split(":")
        // text.text = `${texts.length > 1? texts[0]+":" : ''}(${x},${y})`
        text.set({
            left: x,
            top: y
        });
        canvas.renderAll();
    });
}


const rectangle = () => {
    let isDrawing = false,text = null,rect = null,startPoint = null,moveX = 0,moveY = 0,id = null;
    // 有数据则
    // 监听鼠标按下事件
    canvas.on('mouse:down', function (options) {
        // 检查是否有选中的对象，如果有则不创建新框
        if (canvas.getActiveObject()) {
            return;
        }
        id = generateShortUuid()
        isDrawing = true;
        startPoint = options.pointer;
        // 禁用对象选择
        canvas.selection = false;
        rect = new fabric.Rect({
            id: id,
            left: startPoint.x,
            top: startPoint.y,
            width: 0,
            height: 0,
            stroke: 'red',
            fill: 'transparent',
            strokeWidth: 1,
            selectable: true
        });
        // 创建文本对象
        let x = Math.floor(startPoint.x)
        let y = Math.floor(startPoint.y - 20)
        text = new fabric.Text(`参数`, {
            id: id,
            left: x,
            top: y, // 将文本放置在矩形上方
            fill: 'red',
            fontSize: 15,
            selectable: false
        });
        canvas.add(rect,text);
        // rect 缩放事件，移动事件
        rectEvent(rect,text)
    });
    // 监听鼠标移动事件
    canvas.on('mouse:move', function (options) {
        if (isDrawing) {
            const currentPoint = options.pointer;
            moveX = currentPoint.x - startPoint.x;
            moveY = currentPoint.y - startPoint.y;
            rect.set({
                width: moveX,
                height: moveY
            });
            if(text){
                // 更新文本位置
                let x = Math.floor(rect.left)
                let y = Math.floor(rect.top - 20)
                // let texts = text.text.split(":")
                // text.text = `${texts.length > 1? texts[0]+":" : ''}(${x},${y})`
                text.set({
                    left: x,
                    top: y
                });
            }
            canvas.renderAll();
        }
    });
    // 监听鼠标松开事件
    canvas.on('mouse:up', function () {
        // 防止误按
        if(isDrawing && (Math.abs(moveX) < 3 || Math.abs(moveY) < 3)){
            canvas.remove(rect,text);
            rect = null
        }
        isDrawing = false;
        if (rect) {
            // 计算框相对于图片的坐标
            const relativeLeft = (rect.left - offsetX) / scaleFactor;
            const relativeTop = (rect.top - offsetY) / scaleFactor;
            // 计算框相对于图片的宽度和高度
            const relativeWidth = rect.width / scaleFactor;
            const relativeHeight = rect.height / scaleFactor;
            let item = imageItems.value.find(item => item.id === rect.id)
            if(item){
                return
            }
            // 选中当前框
            canvas.setActiveObject(rect);
            canvas.selection = true;
            canvas.renderAll();
            imageItems.value.push({
                id: rect.id,
                typeID: null,
                featuresName: null,
                x: Math.floor(relativeLeft),
                y: Math.floor(relativeTop),
                width: Math.floor(relativeWidth),
                height: Math.floor(relativeHeight)
            });
            moveX = 0
            moveY = 0
            // console.log('框相对于图片的坐标 (x, y):', relativeLeft, relativeTop);
            // console.log('框相对于图片的宽度:', relativeWidth);
            // console.log('框相对于图片的高度:', relativeHeight);
        }
    });
}

const drawBg = (url) => {
    // 绘制背景前清空画布
    // fabricCanvas.clear();
    // bgImage为图片路径
    fabric.Image.fromURL(url, function(img) {
        // 获取画布的宽度和高度
        const canvasWidth = canvas.getWidth();
        const canvasHeight = canvas.getHeight();
        // 获取图片的原始宽度和高度
        const imageWidth = img.width;
        const imageHeight = img.height;
        // 计算图片的宽高比
        const imageAspectRatio = imageWidth / imageHeight;
        // 计算画布的宽高比
        const canvasAspectRatio = canvasWidth / canvasHeight;
        if (imageAspectRatio > canvasAspectRatio) {
            // 如果图片的宽高比大于画布的宽高比，按宽度进行缩放
            scaleFactor = canvasWidth / imageWidth;
        } else {
            // 否则按高度进行缩放
            scaleFactor = canvasHeight / imageHeight;
        }
        // 计算缩放后的图片宽度和高度
        const scaledImageWidth = imageWidth * scaleFactor;
        const scaledImageHeight = imageHeight * scaleFactor;
        // 计算水平和垂直方向的偏移量，以实现居中
        offsetX = (canvasWidth - scaledImageWidth) / 2;
        offsetY = (canvasHeight - scaledImageHeight) / 2;
        // 设置背景图片，并设置其透明度
        canvas.setBackgroundImage(img, canvas.renderAll.bind(canvas), {
            opacity: 1,
            // scaleX: scaleFactor,
            // scaleY: scaleFactor,
            left: offsetX,
            top: offsetY
        });
        img.scale(scaleFactor);
        // img.set({ left: offsetX, top: offsetY });
        // canvas.add(img);
        canvas.renderAll();
        restWindow(img)
        // 保存绘制背景图片的操作历史
        // fabricCanvas._historySaveAction();
    }, {crossOrigin: 'anonymous'});
}

const onDelRect = (val,idx) => {
    // 获取画布上的所有对象
    const allObjects = canvas.getObjects();
    // 筛选出所有的矩形对象
    const element = allObjects.filter(obj => ((obj instanceof fabric.Rect || obj instanceof fabric.Text) && obj.id === val.id ))
    if(element.length < 1){
        return
    }
    element.forEach(item => canvas.remove(item))
    imageItems.value.splice(idx, 1);
}

const generateShortUuid = () => {
    const timestamp = new Date().getTime().toString(36);
    const fullUuid = crypto.randomUUID();
    return fullUuid.replace(/-/g, '').slice(0, 8)+timestamp; // 取前 8 位
}

const onChangeFeaturesName = (fid,iid) => {
    let features = featuresList.value.find(item => item.id === fid)
    const element = canvas.getObjects().filter(obj => (obj instanceof fabric.Text) && obj.id === iid )
    let find = imageItems.value.find(item => item.id === iid);
    find.featuresName = features.featuresName
    element.forEach(item => item.text = `${features.featuresName}`)
    canvas.renderAll();
}

const onSave = () => {
    for (let i = 0; i < imageItems.value.length; i++) {
        let image = imageItems.value[i]
        if(!image.typeID || !image.featuresName){
            message.warning("请给标注添加特征值")
            return
        }
        if(image.x < 0 || image.y < 0){
            message.warning("坐标不能小于0，移动框重新获取坐标")
            return
        }
    }
    emits('onSave',imageItems.value)
}

const onClose = () => {
    imageItems.value.length = 0
    props.editImageItem.show = false
}

onMounted(() => {
    props.editImageItem.title = '图像物体标注'
    props.editImageItem.width = '60%'
})

defineExpose({show,onClose})
</script>

<template>
    <el-row v-if="editImageItem.show" :class="{'dialog-modal':true,'dialog-menu-fold': menuFoldState}">
        <el-button style="position: absolute;top: 0;right: 0" type="primary"
                   :icon="CloseBold" @click="onClose" />
        <el-row class="main">
            <el-row id="canvas-row" style="width: 70%;height: 100%" class="left">
                <canvas id="canvas" />
            </el-row>
            <el-row class="right" style="width: 30%;height: 100%;">
                <el-row class="image-list" >
                    <el-card :key="idx" :body-style="{ padding: '10px' }" v-for="(item,idx) in imageItems" style="height: 80px;width: 100%;">
                        <el-form :key="idx" :model="editImageItem" style="width: 100%;height: 100%" label-width="70" label-position="left">
                            <el-row style="width: 100%;" >
                                <el-form-item label="特征信息" style="width: 100%">
                                    <el-select
                                        v-model="item.typeID"
                                        clearable
                                        @change="e => onChangeFeaturesName(e, item.id)"
                                    >
                                        <el-option
                                            v-for="item in featuresList"
                                            :key="item.id"
                                            :label="item.featuresName"
                                            :value="item.id"
                                        />
                                    </el-select>
                                    <el-input style="display: none;" v-model="item.featuresName" />
                                    <el-row style="width: 100%;font-size: 12px">
                                        <el-col :span="10">
                                            原点(x,y)=({{item.x}},{{item.y}})
                                        </el-col>
                                        <el-col :span="12">
                                            框选(宽,高)=({{ item.width+','+item.height }})
                                        </el-col>
                                        <el-col :span="2">
                                            <el-row style="width: 100%;padding-top: 5px" justify="end">
                                                <el-button type="danger" size="small" :icon="Delete" circle @click="onDelRect(item,idx)" />
                                            </el-row>
                                        </el-col>
                                    </el-row>
                                </el-form-item>
                            </el-row>
                        </el-form>
                    </el-card>
                </el-row>
                <el-button type="primary" class="save" @click="onSave">
                    保存标注信息
                </el-button>
            </el-row>
        </el-row>
    </el-row>
</template>

<style scoped lang="scss">
.dialog-menu-fold {
    left: 63px !important;
    width: calc(100% - 63px) !important;
    transition-property: left, width;
    transition-duration: .4s;
    transition-timing-function: ease;
}
.dialog-modal{
    width: calc(100% - 200px);
    height: calc(100vh - 59px);
    top: 59px;
    left: 200px;
    position: absolute;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 10;
}
:deep(.dialog-row-content){
    height: 100% !important;
    overflow-y: hidden;
}
.main{
    height: 85%;
    width: 85%;
    margin: auto;
    background-color: #0a0a0a;
    float: left;
}
.save{
    width: 100%;
    height: 40px;
}
:deep(.el-button){
    border-radius: 0 !important;
}
.image-list{
    width: 100%;
    height: calc(100% - 40px);
    padding: 5px;
    overflow-y: auto;
    flex-direction: row;
    gap: 5px;
    align-content: start;
    background-color: var(--el-bg-color);
}
</style>